
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author:刘赵强
 * @Date:2021/3/15 14:42
 * @Description:
 **/
class MyData{
    volatile int number = 0;

    AtomicInteger num = new AtomicInteger();

    public void addTo60(){
        this.number = 60;
    }

    public void addPlusPlus(){
        number++;
        num.addAndGet(1);
    }
}

/**
 * 1.验证Volatile的可见性
 *   1.1假如 int number = 0;number变量之前根本没有添加volatile关键字修饰，没有可见性
 *   1.2 添加了volatile 可以解决可见性问题
 *
 * 2 验证volatile不保证原子性
 *  2.1原子性指的是什么意思？
 *    不可分隔，完整性 也即某个线程正在做某个具体业务时 中间不可以被加赛或者被分隔 需要整体完整 要不同时成功  要么同时失败
 *
 *  2.2 volatile 不保证原子性的案例演示
 *     1.加synchronized
 *     2.AtomicInteger
 *
 */
public class VolatileDemo {
    /**
     * 验证可见性
     * volatile可以保证可见性 及时通知其他线程 主物理内存的值已修改。
     */
    @Test
    public void seeOkByVolatile(){
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t"+myData.number);
            try {
                TimeUnit.SECONDS.sleep(3);
            }catch(Exception e){
                e.printStackTrace();
            }
            myData.addTo60();
            System.out.println(Thread.currentThread().getName()+"\t"+myData.number);
        }, "thread AA").start();

        while(myData.number == 0){
            //System.out.println("main线程一直在这里循环，直到number值不在等于零");
            //当number没有用volatile修饰的时候 会在这里一直循环 因为线程AA修改值后没有通知mian线程 所有主线程中的number值一直是0
            //当number用volatile修饰的时候 3秒后会 打印 mission is over 这句话.说明线程AA修改的值 在mian线程中可见了  验证了volatile的可见性
        }

        System.out.println(Thread.currentThread().getName()+"\t mission is over");
    }

    /**
     * volatile不具有原子性
     */
    @Test
    public void yuanzixing(){
        MyData myData = new MyData();
        for(int i=0;i<20;i++){
            new Thread(() -> {
                for(int j=0;j<1000;j++) {
                    myData.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }
        //需要等待20个线程全都计算完成后 在用main现在取地最终的结果是多少
        while(Thread.activeCount()>2){//mian线程 GC线程
            Thread.yield();
        }
        //验证后 最终结果不等于20000
        System.out.println("finaly i=="+myData.number);
        System.out.println("finaly i=="+myData.num.get());
    }
}